/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * main.cc
 * Copyright (C) Jeff Goodhue 2011 <jeff@JeffLinux>
 * 
 * Calculator is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Calculator is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <gtkmm.h>
#include <iostream>
#include "config.h"
#include "nullable-dbl.h" //my home made class, just an double and a boolean.
#include <boost/lexical_cast.hpp> //wonderful api to convert a string to a different type and vise-versa
#ifdef ENABLE_NLS
#  include <libintl.h>
#endif

#define KP_Multiply 0xFFAA //I could not figure out how to include the GTK header for these constanats... so I just declared them myself.
#define KP_Add 0xFFAB
#define KP_Subtract 0xFFAD
#define KP_Decimal 0xFFAE
#define KP_Divide 0xFFAF
#define KP_0 0xFFB0
#define KP_1 0xFFB1
#define KP_2 0xFFB2
#define KP_3 0xFFB3
#define KP_4 0xFFB4
#define KP_5 0xFFB5
#define KP_6 0xFFB6
#define KP_7 0xFFB7
#define KP_8 0xFFB8
#define KP_9 0xFFB9

#define UI_FILE "src/Calculator.glade"

Gtk::Window *main_win = 0; //These two widgets will be called outside
Gtk::Entry *calcText = 0; //int main
bool wipe = false; //if wipe is true then the next number entered will rewrite the textbox.
NullableDbl firstDbl; //in order to store the first variable
char operation = '+'; //stores the opertation.

static void numPress(Glib::ustring str) { //in order to simplify all the functions
	Glib::ustring calcString = calcText->get_text();
	main_win->unset_focus();
	if(calcString.compare("0") == 0 || wipe) {
		calcString = str;
		wipe = false;
	}
	else if(calcString.length() < 17){
			calcString += str;
	}
	calcText->set_text(calcString);	
}

static void solve() {
	double secDbl = boost::lexical_cast<double>(calcText->get_text().c_str());
	Glib::ustring resultString = "";
	switch(operation) {
	case '+':
		resultString = boost::lexical_cast<std::string>(firstDbl.getDbl() + secDbl);
		break;
	case '-':
		resultString = boost::lexical_cast<std::string>(firstDbl.getDbl() - secDbl);
		break;
	case '*':
		resultString = boost::lexical_cast<std::string>(firstDbl.getDbl() * secDbl);
		break;
	case '/':
		resultString = boost::lexical_cast<std::string>(firstDbl.getDbl() / secDbl);
		break;
	default:
		resultString = "ERROR";
		break;
	}
	calcText->set_text(resultString);
	wipe = true;
}

static void opPress(char op) {
	if(firstDbl.getNull()) {
		firstDbl.setDbl(boost::lexical_cast<double>(calcText->get_text().c_str()));
		firstDbl.setNull(false);
		operation = op;
		wipe = true;
	} else {
		solve();
		firstDbl.setDbl(boost::lexical_cast<double>(calcText->get_text().c_str()));
		operation = op;
	}
}

static void butEnterPress() {
	if(!firstDbl.getNull())
	{
		solve();
		firstDbl.setDbl(boost::lexical_cast<double>(calcText->get_text().c_str()));
		firstDbl.setNull(true);
	}
}

static void butClearPress() {
	firstDbl.setDbl(0);
	firstDbl.setNull(true);
	calcText->set_text("0");
}

static void butDecPress() {
	Glib::ustring calcString = calcText->get_text();
	if(wipe) {
		calcString = "0.";
		wipe = false;
	}
	else if(calcString.find(".") == Glib::ustring::npos){
		calcString += ".";
	}
	calcText->set_text(calcString);
}

bool keyPressed(GdkEventKey *event) {
	if(!event)
		return false;
	switch(event->keyval) { //case events are ASCII codes for numbers 0-9.
	case 48:
	case 49:
	case 50:
	case 51:
	case 52:
	case 53:
	case 54:
	case 55:
	case 56:
	case 57:
			//subtract the index code to make this easier
			numPress(boost::lexical_cast<std::string>(event->keyval - 48));//Calls numPress like a button was pressed.
			return true;
	case KP_0:
	case KP_1:
	case KP_2:
	case KP_3:
	case KP_4:
	case KP_5:
	case KP_6:
	case KP_7:
	case KP_8:
	case KP_9:
			//subtract the index code to make this easier
			numPress(boost::lexical_cast<std::string>(event->keyval - 65456));//Calls numPress like a button was pressed.
			return true;
	case KP_Add:
			opPress('+');
			return true;
	case KP_Multiply:
			opPress('*');
			return true;
	case KP_Subtract:
			opPress('-');
			return true;
	case KP_Divide:
			opPress('/');
			return true;
	case KP_Decimal:
			butDecPress();
			return true;
	}
	return false;
}

int main (int argc, char *argv[])
{
	Gtk::Main kit(argc, argv);


	//Load the Glade file and instiate its widgets:
	Glib::RefPtr<Gtk::Builder> builder; //The builder is used to connect each widget from the glade file
	try
	{
		builder = Gtk::Builder::create_from_file(UI_FILE);
	}
	catch (const Glib::FileError & ex)
	{
		std::cerr << ex.what() << std::endl;
		return 1;
	}
	builder->get_widget("calcMain", main_win); //connects the window to the widget
	if (main_win)
	{
		builder->get_widget("calcTxt", calcText);
		if(calcText) {
			Gtk::Button *butAdd = 0, *butSub = 0, *butMult = 0, *butDev = 0, *butDec = 0, *butEnter = 0, *butClear = 0; //Decalres all non-number buttons
			Gtk::Button* numArr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //Declares and array for all number buttons
			for(int i = 0; i < 10; i++) { //for loop in order to bind all the buttons in the array at once.
				builder->get_widget("but" + boost::lexical_cast<std::string>(i), numArr[i]);
				if(numArr[i])
					numArr[i]->signal_clicked().connect(sigc::bind<Glib::ustring>(sigc::ptr_fun(numPress), boost::lexical_cast<std::string>(i)));
			}
			builder->get_widget("butAdd", butAdd);
			builder->get_widget("butSub", butSub);
			builder->get_widget("butMult", butMult);
			builder->get_widget("butDev", butDev);
			builder->get_widget("butDec", butDec);
			builder->get_widget("butEnter", butEnter);
			builder->get_widget("butClear", butClear);
			if(butAdd) {
				butAdd->signal_clicked().connect(sigc::bind<char>(sigc::ptr_fun(opPress), '+'));
			}
			if(butSub) {
				butSub->signal_clicked().connect(sigc::bind<char>(sigc::ptr_fun(opPress), '-'));
			}
			if(butMult) {
				butMult->signal_clicked().connect(sigc::bind<char>(sigc::ptr_fun(opPress), '*'));
			}
			if(butDev) {
				butDev->signal_clicked().connect(sigc::bind<char>(sigc::ptr_fun(opPress), '/'));
			}
			if(butDec) {
				butDec->signal_clicked().connect(sigc::ptr_fun(butDecPress));
			}
			if(butEnter) {
				butEnter->signal_clicked().connect(sigc::ptr_fun(butEnterPress));
			}
			if(butClear) {
				butClear->signal_clicked().connect(sigc::ptr_fun(butClearPress));
			}
			main_win->signal_key_press_event().connect(sigc::ptr_fun(keyPressed), false);//connects event to entire window.
		}
		kit.run(*main_win);
	}
	return 0;
}
